/***************************************************************************
 *   Copyright (C) 2009 by Zheng Yonglei
 *   zhengyonglei@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "ReciteAnalysisDialog.h"

TestResults::TestResults()
{
	reset();
}

void TestResults::reset()
{
	map<long, vector<int>*>::iterator result_it;
	for (result_it = mResultGroup.begin(); result_it != mResultGroup.end(); result_it++)
		delete result_it->second;
	mResultGroup.clear();
	mPassFirstTestRate.clear();
	mPassLastTestRate.clear();
}

void TestResults::addResult(int reciteCount, int interval, TestResultType testResult, int wordID)
{
	long descriptor = reciteCount*TEST_RES_COUNT_RATE + interval*TEST_RES_INT_RATE + int(testResult);
	//map<int, vector<int>*>::iterator result_it;
	if (mResultGroup.find(descriptor) == mResultGroup.end())
		mResultGroup.insert(make_pair(descriptor, new vector<int>));

	//cout<<"TestResults::addResult-->descriptor = "<<descriptor<<"\twordID = "<<wordID<<endl;
	mResultGroup[descriptor]->push_back(wordID);
}

bool TestResults::getWordIDs(int reciteCount, int interval, TestResultType testResult, vector<int>& wordIDs)
{
	long descriptor = reciteCount*TEST_RES_COUNT_RATE + interval*TEST_RES_INT_RATE + int(testResult);
	if (mResultGroup.find(descriptor) == mResultGroup.end())
		return false;
	else
	{
		vector<int>& result = *(mResultGroup[descriptor]);
		int count = result.size();
		wordIDs.clear();
		for (int i=0; i<count; i++)
			wordIDs.push_back(result[i]);
		return true;
	}
}

void TestResults::fitCurve()
{
	//cout<<"TestResults::fitCurve"<<endl;
	map<long, vector<int>*>::iterator result_it;
	vector<long> resultVector;
	resultVector.clear();
	long lastGroup = -1;
	for (result_it = mResultGroup.begin(); result_it != mResultGroup.end(); result_it++)
	{
		long curGroup = result_it->first - result_it->first%(TEST_RES_INT_RATE);
		//cout<<"TestResults::fitCurve-->curGroup="<<curGroup<<endl;
		if (lastGroup != curGroup)
		{
			resultVector.push_back(curGroup);
			lastGroup = curGroup;
		}
	}

	QString queryCommand;
	QString path = "./data/users/" + DB.mUsrMain + "/";
	QFile file(path+ QString("ForgetCurveData.m"));
	if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
		return;

	QTextStream out(&file);

	//fit memory curve
	out<<"%forget_curve[recite_count, interval, pass_first_count, fail_first_count, pass_first_rate]"<<endl;
	out<<"forget_curve=["<<endl;
	for (int i=0; i<resultVector.size(); i++)
	{
		//cout<<"TestResults::fitCurve-->resultVector["<<i<<"]="<<resultVector[i]<<endl;
		long curGroup = resultVector[i];
		float passFirstCount =0.0;
		float failFirstCount =0.0;
		float passLastCount =0.0;
		float failLastCount =0.0;

		result_it = mResultGroup.find(curGroup + int(TRT_Pass_First));
		if (result_it != mResultGroup.end())
			passFirstCount = result_it->second->size();

		result_it = mResultGroup.find(curGroup + int(TRT_Fail_First));
		if (result_it != mResultGroup.end())
			failFirstCount = result_it->second->size();

		result_it = mResultGroup.find(curGroup + int(TRT_Pass_Last));
		if (result_it != mResultGroup.end())
			passLastCount = result_it->second->size();

		result_it = mResultGroup.find(curGroup + int(TRT_Fail_Last));
		if (result_it != mResultGroup.end())
			failLastCount = result_it->second->size();

		int reciteCount = curGroup/TEST_RES_COUNT_RATE;
		int interval = (curGroup%TEST_RES_COUNT_RATE)/TEST_RES_INT_RATE;

		if (passFirstCount + failFirstCount > 0)
		{
			mPassFirstTestRate[curGroup]=passFirstCount/(passFirstCount+failFirstCount);
			//cout<<"reciteCount:"<<reciteCount<<"\tinterval:"
			//	<<interval<<"\tpassFirstRate="<<mPassFirstTestRate[curGroup]
			//	<<"(total:"<<passFirstCount + failFirstCount<<")"<<endl;
		}
		out<<reciteCount<<"\t"<<interval<<"\t"<<passFirstCount<<"\t"<<failFirstCount<<"\t"<<mPassFirstTestRate[curGroup]<<endl;

		if (passLastCount + failLastCount > 0)
		{
			mPassLastTestRate[curGroup]=float(passLastCount/(passLastCount+failLastCount));
			//cout<<"reciteCount:"<<reciteCount<<"\tinterval:"
			//	<<interval<<"\tpassLastRate="<<mPassLastTestRate[curGroup]
			//	<<"(total:"<<passLastCount + failLastCount<<")"<<endl;
		}
	}
	out<<"];"<<endl;
}

int TestResults::getWordCount(int reciteCount, int interval, TestResultType testResult)
{
	long descriptor = reciteCount*TEST_RES_COUNT_RATE + interval*TEST_RES_INT_RATE + int(testResult);
	if (mResultGroup.find(descriptor) == mResultGroup.end())
		return -1;
	else
	{
		vector<int>& result = *(mResultGroup[descriptor]);
		int count = result.size();
		return count;
	}
}

SelectPolicy::SelectPolicy(QString name):
	name(name),
	difficultyLevelMax(0),
	difficultyLevelMin(9999),
	activeFinishedWordCountMax(-1),
	activeRecitingWordsCountMax(-1),
	activeUntouchedWordsCountMax(-1)
{
}

ScanRange::ScanRange(QString name):
	name(name),
	redBibleBeginList(1),
	redBibleEndList(41)
{

}

ReciteAnalysisDialog::ReciteAnalysisDialog(QDialog *parent)
    : QDialog(parent)
{
	ui.setupUi(this);

	ui.listBeginInput->setRange(0, 999);
	ui.listEndInput->setRange(0, 999);
	ui.quickSelectWordCountInput->setRange(0, 9999);
	ui.difficultyLevelMinInput->setRange(0, 9999);
	ui.difficultyLevelMaxInput->setRange(0, 9999);
	ui.activeFinishedWordsCount->setRange(0, 9999);
	ui.activeRecitingWordsCount->setRange(0, 9999);
	ui.activeUntouchedWordsCount->setRange(0, 9999);

	ui.scanRangeComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
	ui.selectPolicyBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);

	mTestResult = new TestResults();

	QObject::connect(ui.quickSelectButton, SIGNAL(clicked()), this, SLOT(quickSelectDifficultWords()));

	//QShortcut* closeShortcut = new QShortcut(QKeySequence(Qt::Key_Escape),  this);
	//QObject::connect(closeShortcut,SIGNAL(activated()),this,SLOT(hide()));

	QObject::connect(ui.scanReviewWordsButton, SIGNAL(clicked()), this, SLOT(scan()));
	QObject::connect(ui.closeDialogButton, SIGNAL(clicked()), this, SLOT(accept()));
	ui.scanResultTableView->setEditTriggers(false);
	ui.scanResultTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
	ui.scanResultTableView->setAlternatingRowColors(true);

	ui.selectedWordGroupTableView->setEditTriggers(false);
	ui.selectedWordGroupTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
	ui.selectedWordGroupTableView->setAlternatingRowColors(true);

	QObject::connect(ui.selectPolicyBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectPolicyChanged(int)));
	QObject::connect(ui.selectWordButton, SIGNAL(clicked()), this, SLOT(selectWordsToRecite()));
	QObject::connect(ui.scanRangeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(scanRangeChanged(int)));
	QObject::connect(ui.reciteWordOKButton, SIGNAL(clicked()), this, SLOT(recite()));
	QObject::connect(ui.browseWordOKButton, SIGNAL(clicked()), this, SLOT(browse()));
	//QObject::connect(ui.closeDialogButton, SIGNAL(clicked()), this, SLOT(hide()));

	mWordDateModel = new QStandardItemModel(this);
	mCandidatesModel = new QStandardItemModel(this);

	mReciteAnalysisPlot = new ReciteAnalysisPlot();
	QObject::connect(ui.showGraphButton, SIGNAL(clicked()), this, SLOT(showGraph()));

	QAction* selectRowButton = new QAction(QIcon("./pixmaps/arrow-right.png"), tr("Select a Row as Candidates"), this);
	connect(selectRowButton, SIGNAL(triggered()), this, SLOT(selectRowAsCandidates()));
	QAction* removeRowButton = new QAction(QIcon("./pixmaps/arrow-left.png"), tr("Remove a Row as Candidates"), this);
	connect(removeRowButton, SIGNAL(triggered()), this, SLOT(removeRowAsCandidates()));
	QAction* clearAllRowButton = new QAction(QIcon("./pixmaps/list-remove.png"), tr("Remove All Candidates"), this);
	connect(clearAllRowButton, SIGNAL(triggered()), this, SLOT(clearAllRowCandidates()));
	ui.selectRowButton->setDefaultAction(selectRowButton);
	ui.removeRowButton->setDefaultAction(removeRowButton);
	ui.clearAllRowButton->setDefaultAction(clearAllRowButton);

	loadSelectPolicies();
	loadScanRange();

	//ui.difficultyLevelMaxInput->setText(QString("%1").arg(999999));
	//ui.difficultyLevelMinInput->setText(QString("%1").arg(10));
	ui.quickSelectWordCountInput->setValue(OPT.quickSelectWordCount);
}

ReciteAnalysisDialog::~ReciteAnalysisDialog()
{

}

void ReciteAnalysisDialog::showThisDialog()
{
	int width = this->width();
	float leftWidthRatio = 0.5f;
	QList<int> sizes;
	sizes<<width*leftWidthRatio<<width*(1.0f-leftWidthRatio);
	ui.splitter->setSizes(sizes);
	//scan();
	this->exec();
}

void ReciteAnalysisDialog::showGraph()
{
	float sizeRate = 0.8f;
	mReciteAnalysisPlot->setGeometry(
				this->geometry().x()+(1-sizeRate)*this->geometry().width()/2,
				this->geometry().y()+(1-sizeRate)*this->geometry().height()/2,
				this->geometry().width()*sizeRate,
				this->geometry().height()*sizeRate);

	mReciteAnalysisPlot->exec();
}

void ReciteAnalysisDialog::scan()
{
	cout<<"ReciteDateAnalysis::scan"<<endl;
	//ui.selectWordButton->setEnabled(true);
	ui.selectResultsDisplay->clear();
	int listBegin = ui.listBeginInput->value();
	int listEnd = ui.listEndInput->value();

	mRecitingWordIDs.clear();
	mNewWordIDs.clear();
	mFinishedWordIDs.clear();
	mFinishedWordGroupCount.clear();
	mFinalIDs.clear();
	mTestResult->reset();
	mReviewDateCount.clear();
	mReciteNewDateCount.clear();
	mWordRecitProgress.clear();
	//mQuickSelectWords.clear();

	map<int,int> intervalMap; //used to update table analysis
	map<int,int> reciteCountMap; //used to update table analysis
	map<int, int> difficultyLevelMap; //used to update column history.difficulty_level
	bool calculateDifficultyLevel=false;
	QDate today = QDate::currentDate();

	QString queryCommand;
	queryCommand = QString("select count(id) from words;");
	DB.quickGet(queryCommand);
	int wordCount = DB.getTableValue(0,"count(id)").toInt();

	queryCommand.clear();
	queryCommand = QString(
			"select history.id,"
			"history.recite_date_history,"
			"history.difficulty_level,"
			"history.last_reciting_status "
			" from history,lists ");
	queryCommand.append(QString(" where history.id=lists.id and lists.red_bible_list>=%1").arg(listBegin));
	queryCommand.append(QString(" and lists.red_bible_list<=%1;").arg(listEnd));
	DB.quickGet(queryCommand);
	for(int j=0; j<DB.getRowCount(); j++)
	{
		//cout<<"j="<<j<<endl;
		//parse word history info
		int id = DB.getTableValue(j,"id").toInt();
		//cout<<"ReciteAnalysisDialog::scan-->id="<<id<<endl;
		int interval;
		int reciteCount;
		int difficultyLevel = DB.getTableValue(j,"difficulty_level").toInt();
		QDate lastDate;
		QString lastDateString;
		QString reciteDateString = DB.getTableValue(j,"recite_date_history");
		QStringList dateHistoryList = reciteDateString.split("|", QString::SkipEmptyParts);

		if(!dateHistoryList.isEmpty())
		{
			lastDateString = dateHistoryList.last();
			lastDateString = lastDateString.left(lastDateString.lastIndexOf(':'));
			lastDate = QDate::fromString(lastDateString, "yyyy-MM-dd");
			interval = lastDate.daysTo(today);
			reciteCount = dateHistoryList.size();
		}
		else
		{
			interval = -1;
			reciteCount = 0;
		}

		intervalMap[id] = interval;
		reciteCountMap[id] =  reciteCount;

		//if (interval > 0 && reciteCount >0)
		//	mQuickSelectWords[difficultyLevel].push_back(id);

		//push back reciting words
		if(DB.getTableValue(j,"last_reciting_status").toInt() == int(WS_Reciting))
			mRecitingWordIDs.push_back(id);

		//push back untouched words
		if(DB.getTableValue(j,"last_reciting_status").toInt() == int(WS_Untouched))
			mNewWordIDs.push_back(id);

		//classify finished words
		if(	DB.getTableValue(j,"last_reciting_status").toInt() == int(WS_Finished)
		||	DB.getTableValue(j,"last_reciting_status").toInt() == int(WS_Reviewing))
		{
			mFinishedWordIDs.push_back(id);
			long descriptor = interval*WORD_DES_INT_RATE + reciteCount;
			map<long, int>::iterator count_it = mFinishedWordGroupCount.find(descriptor);
			if (count_it == mFinishedWordGroupCount.end())
				mFinishedWordGroupCount[descriptor]=1;
			else
				mFinishedWordGroupCount[descriptor]++;

			//bool calculateDifficultyLevel=true;
			if (calculateDifficultyLevel)
			{
				Word* word = new Word();
				word->setReciteDateHistory(reciteDateString);
				word->refineDifficultyLevel();
				int difficulty_level = word->getDifficultyLevel();
				delete word;

				difficultyLevelMap[id]=difficulty_level;
				//queryCommand.clear();
				//queryCommand.append(QString("update history set difficulty_level=%1 ").arg(difficulty_level));
				//queryCommand.append(QString("where id=%1;").arg(id));
				//DB.quickUpdate(queryCommand);
			}

				//cout<<"ReciteAnalysisDialog::scan-->word["<<id<<"]:"<<reciteDateString.toUtf8().data()<<endl;
			//analysis all history records
			for (int k=0; k<dateHistoryList.size(); k++)
			{
				map<int,int>::iterator count_it = mWordRecitProgress.find(k+1);
				if (count_it == mWordRecitProgress.end())
					mWordRecitProgress[k+1]=1;
				else
					mWordRecitProgress[k+1]++;

				//cout<<"ReciteAnalysisDialog::scan-->dateHistoryList["<<id<<"]:"<<dateHistoryList[k].toUtf8().data()<<endl;
				QString historyString = dateHistoryList[k];
				QString historyDateString = historyString.left(historyString.indexOf(':'));
				QDate historyDate = QDate::fromString(historyDateString, "yyyy-MM-dd");
				QString historyRemForString = historyString.right(historyString.size() - historyString.indexOf(":") -1);

				if (k == 0)
				{
					map<QDate, int>::iterator count_it = mReciteNewDateCount.find(historyDate);
					if (count_it == mReciteNewDateCount.end())
						mReciteNewDateCount[historyDate]=1;
					else
						mReciteNewDateCount[historyDate]++;
				}
				else
				{
					map<QDate, int>::iterator count_it = mReviewDateCount.find(historyDate);
					if (count_it == mReviewDateCount.end())
						mReviewDateCount[historyDate]=1;
					else
						mReviewDateCount[historyDate]++;
				}

				if (k > 0)
				{
					QString lastHistoryString = dateHistoryList[k-1];
					QString lastHistoryDateString = lastHistoryString.left(lastHistoryString.indexOf(':'));
					QDate lastHistoryDate = QDate::fromString(lastHistoryDateString, "yyyy-MM-dd");
					int historyInt = lastHistoryDate.daysTo(historyDate);

					if (historyRemForString[0] == 'Y')
						mTestResult->addResult(k+1, historyInt, TRT_Pass_First, id);
					else
						mTestResult->addResult(k+1, historyInt, TRT_Fail_First, id);

					if (historyRemForString.lastIndexOf('Y') > historyRemForString.lastIndexOf('N'))
						mTestResult->addResult(k+1, historyInt, TRT_Pass_Last, id);
					else
						mTestResult->addResult(k+1, historyInt, TRT_Fail_Last, id);
				}
			}
		}
	}

	DB.quickUpdate("BEGIN");
	if (calculateDifficultyLevel)
	{
		cout<<"ReciteAnalysisDialog::scan-->update difficulty level"<<endl;
		for (	map<int,int>::iterator diff_it = difficultyLevelMap.begin();
				diff_it != difficultyLevelMap.end();
				diff_it++)
		{
			int id = diff_it->first;
			int diffLevel = difficultyLevelMap[id];
			queryCommand.clear();
			queryCommand.append(QString("update history set difficulty_level=%1 ").arg(diffLevel));
			queryCommand.append(QString("where id=%1;").arg(id));
			DB.quickUpdate(queryCommand);
		}
	}
	DB.quickUpdate("COMMIT");

	assert(intervalMap.size() == reciteCountMap.size());
	//cout<<"update analysis table"<<endl;
	DB.quickUpdate("BEGIN");
	for (	map<int,int>::iterator int_it = intervalMap.begin();
			int_it != intervalMap.end();
			int_it++)
	{
		int id = int_it->first;
		int interval = intervalMap[id];
		int reciteCount = reciteCountMap[id];

		queryCommand.clear();
		queryCommand.append(QString("update analysis set last_interval=%1, ").arg(interval));
		queryCommand.append(QString("recite_count=%1 ").arg(reciteCount));
		queryCommand.append(QString("where id=%1;").arg(id));
		DB.quickUpdate(queryCommand);
	}
	DB.quickUpdate("COMMIT");

	setScanResultView();
	if (ui.saveDataCheckBox->isChecked())
	{
		mTestResult->fitCurve();
		saveWordRecitProgressMFile();
		saveDifficultyLevelMFile();
		saveReciteDateMFile();
	}
	mReciteAnalysisPlot->drawReciteDateGraph(mReviewDateCount, mReciteNewDateCount);
	mReciteAnalysisPlot->drawDifficultyLevelDistribution(mDiffLevelDist);
	mReciteAnalysisPlot->drawReciteProgress(mWordRecitProgress);
}

/*void ReciteAnalysisDialog::saveGrossPassRateMFile()
{
	cout<<"ReciteAnalysisDialog::saveGrossPassRateMFile"<<endl;
	QString queryCommand;
	QString path = "./data/users/" + DB.mUsrMain + "/";
	QFile file(path+ QString("GrossPassRateData.m"));
	if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
	 return;

	QTextStream out(&file);

	out<<"gross_pass_rate_count=["<<endl;
	queryCommand = "select gross_pass_rate,count(id) from history group by gross_pass_rate;";
	DB.quickGet(queryCommand);
	for (int i=0; i<DB.getRowCount(); i++)
	{
		double rate = DB.getTableValue(i,"gross_pass_rate").toDouble();
		int count =  DB.getTableValue(i,"count(id)").toInt();
		out<<rate<<"\t"<<count<<endl;
	}
	out<<"];"<<endl;
}*/

void ReciteAnalysisDialog::saveDifficultyLevelMFile()
{
	//cout<<"ReciteAnalysisDialog::saveDifficultyLevelMFile"<<endl;
	mDiffLevelDist.clear();
	QString queryCommand;
	QString path = "./data/users/" + DB.mUsrMain + "/";
	QFile file(path+ QString("DifficultyLevelData.m"));
	if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
	 return;

	QTextStream out(&file);

	out<<"difficulty_level_count=["<<endl;
	queryCommand = QString("select difficulty_level,count(id) from history group by difficulty_level;");
	DB.quickGet(queryCommand);

	for (int i=0; i<DB.getRowCount(); i++)
	{
		int difficulty_level = DB.getTableValue(i,"difficulty_level").toInt();
		int count_id = DB.getTableValue(i,"count(id)").toInt();
		out	<<difficulty_level<<"\t"<<count_id<<endl;
		mDiffLevelDist[difficulty_level] = count_id;
	}
	out<<"];"<<endl;
}

void ReciteAnalysisDialog::saveReciteDateMFile()
{
	QString queryCommand;
	QString path = "./data/users/" + DB.mUsrMain + "/";
	QFile file(path+ QString("ReciteDateData.m"));
	if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
		return;

	QTextStream out(&file);
	//print recite date statistics
	QDate today = QDate::currentDate();
	map<QDate, int>::iterator count_it;
	//cout<<"ReciteAnalysisDialog::scan-->Recite New Date Count:"<<endl;
	out<<"recite_new_count_date=["<<endl;
	for (count_it = mReciteNewDateCount.begin(); count_it != mReciteNewDateCount.end(); count_it++)
	{
		//cout<<count_it->first.toString("yyyy-MM-dd").toUtf8().data()<<": "<<count_it->second<<endl;
		int interval = today.daysTo(count_it->first);
		out<<interval<<"\t"<<count_it->second<<endl;
	}
	out<<"];"<<endl;

	//cout<<"ReciteAnalysisDialog::scan-->Review Date Count:"<<endl;
	out<<"review_count_date=["<<endl;
	for (count_it = mReviewDateCount.begin(); count_it != mReviewDateCount.end(); count_it++)
	{
		//cout<<count_it->first.toString("yyyy-MM-dd").toUtf8().data()<<": "<<count_it->second<<endl;
		int interval = today.daysTo(count_it->first);
		out<<interval<<"\t"<<count_it->second<<endl;
	}
	out<<"];"<<endl;
}

void ReciteAnalysisDialog::saveWordRecitProgressMFile()
{
	QString queryCommand;
	QString path = "./data/users/" + DB.mUsrMain + "/";
	QFile file(path+ QString("WordReciteProgressData.m"));
	if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
		return;

	QTextStream out(&file);
	map<int, int>::iterator count_it;
	//cout<<"saveWordRecitProgressMFile::scan-->Review Progress Count:"<<endl;
	out<<"recite_progress_count=["<<endl;
	for (count_it = mWordRecitProgress.begin(); count_it != mWordRecitProgress.end(); count_it++)
	{
		//cout<<"recite record count:"<<count_it->first<<"\t word count:"<<count_it->second<<endl;
		out<<count_it->first<<"\t"<<count_it->second<<endl;
	}
	out<<"];"<<endl;
}

void ReciteAnalysisDialog::setScanResultView()
{
	//print scan result
	QString scanResult = QString(tr("New word")) + QString(":%1\t").arg(mNewWordIDs.size());
	scanResult.append(QString(tr("Reciting word")) + QString(":%1\t").arg(mRecitingWordIDs.size()));
	scanResult.append(QString(tr("Finished word")) + QString(":%1").arg(mFinishedWordIDs.size()));
	ui.scanResultLabel->setText(scanResult);
	ui.scanResultLabel->setWordWrap(true);

	//set scan result table view
	mWordDateModel->clear();
	mCandidatesModel->clear();
	QStringList headers;
	headers<<tr("ID");
	//headers<<"Last Recite Interval";
	headers<<tr("Interval");
	headers<<tr("Last Recite Date");
	headers<<tr("Recite Count");
	headers<<tr("Word Count");

	ui.scanResultTableView->setModel(mWordDateModel);
	ui.scanResultTableView->verticalHeader()->hide();
	mWordDateModel->setHorizontalHeaderLabels(headers);
	mCandidatesModel->setHorizontalHeaderLabels(headers);

	QHeaderView* scanResultHeader = ui.scanResultTableView->horizontalHeader();
	scanResultHeader->setSortIndicator(0, Qt::AscendingOrder);
	scanResultHeader->setSortIndicatorShown(true);
	scanResultHeader->setClickable(true);
	QObject::connect(scanResultHeader, SIGNAL(sectionClicked(int)), ui.scanResultTableView, SLOT (sortByColumn(int)));

	QHeaderView* selectedWordGroupHeader = ui.selectedWordGroupTableView->horizontalHeader();
	selectedWordGroupHeader->setSortIndicator(0, Qt::AscendingOrder);
	selectedWordGroupHeader->setSortIndicatorShown(true);
	selectedWordGroupHeader->setClickable(true);
	QObject::connect(selectedWordGroupHeader, SIGNAL(sectionClicked(int)), ui.scanResultTableView, SLOT (sortByColumn(int)));

	//int finishedWordCount=0;
	int groupID = 0;
	QDate today = QDate::currentDate();
	map<long, int>::iterator count_it;
	for (	count_it = mFinishedWordGroupCount.begin();
			count_it != mFinishedWordGroupCount.end();
			count_it++)
	{
		groupID++;
		QList<QStandardItem*>finishedWords;
		finishedWords.clear();
		//finishedWords.push_back(new QStandardItem("finished"));
		finishedWords.push_back(new QStandardItem(QString("%1").arg(groupID, 3, 10, QChar('0'))));
		long descriptor = count_it->first;
		int interval = descriptor/WORD_DES_INT_RATE;
		int reciteCount = descriptor%WORD_DES_INT_RATE;
		QDate lastDate = today.addDays(-interval);
		QString lastDateString = lastDate.toString("yyyy-MM-dd");
		int wordCount = count_it->second;
		//finishedWordCount+=wordCount;

		finishedWords.push_back(new QStandardItem(QString("%1").arg(interval, 2, 10, QChar('0'))));
		finishedWords.push_back(new QStandardItem(lastDateString));
		finishedWords.push_back(new QStandardItem(QString("%1").arg(reciteCount, 2, 10, QChar('0'))));
		//finishedWords.push_back(new QStandardItem(QString("%1").arg(wordCount, 4, 10, QChar('0'))));
		finishedWords.push_back(new QStandardItem(QString("%1").arg(wordCount)));
		mWordDateModel->insertRow(mWordDateModel->rowCount(), finishedWords);
	}

	ui.scanResultTableView->resizeColumnsToContents();
}

void ReciteAnalysisDialog::createDefaultSelectPolicyFile()
{
	cout<<"ReciteAnalysisDialog::createDefaultSelectPolicyFile"<<endl;
	QFile file("./configure/ScanPolicy.conf");
	if (file.open(QFile::WriteOnly | QFile::Truncate))
	{
		cout<<"ReciteAnalysisDialog::createDefaultSelectPolicyFile-->write a new policy file."<<endl;
		QTextStream out(&file);
		out<<"[policy]Default"<<endl;
		out<<"red_bible_begin_ist=1"<<endl;
		out<<"red_bible_end_ist=41"<<endl;
		out<<"difficulty_level_max=10"<<endl;
		out<<"difficulty_level_min=9999"<<endl;
		out<<"active_finished_word_count_max=50"<<endl;
		out<<"active_reciting_word_count_max=5"<<endl;
		out<<"active_untouched_word_count_max=0"<<endl;
		out<<endl;
	}
}

void ReciteAnalysisDialog::createDefaultScanRangeFile()
{
	cout<<"ReciteAnalysisDialog::createDefaultScanRangeFile"<<endl;
	QFile file("./configure/ScanRange.conf");
	if (file.open(QFile::WriteOnly | QFile::Truncate))
	{
		cout<<"ReciteAnalysisDialog::createDefaultScanRangeFile-->write a new policy file."<<endl;
		QTextStream out(&file);
		out<<"[policy]Default"<<endl;
		out<<"red_bible_begin_ist=1"<<endl;
		out<<"red_bible_end_ist=41"<<endl;
		out<<endl;
	}
}

void ReciteAnalysisDialog::loadScanRange()
{
	cout<<"ReciteAnalysisDialog::loadScanRange"<<endl;
	mScanRanges.clear();

	QFile file("./configure/ScanRange.conf");
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		cout<<"ReciteAnalysisDialog::loadScanRange-->no ScanRange.conf. Create a new one."<<endl;
		createDefaultScanRangeFile();

		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
		{
			cout<<"ReciteAnalysisDialog::loadScanRange-->failed to create a new ScanRange.conf."<<endl;
			QCoreApplication::quit();
		}
	}

	QTextCodec* codec = QTextCodec::codecForName("utf8");
	QTextStream in(&file);
	in.setCodec(codec);

	int curPolicyID = 0;
	while (!in.atEnd())
	{
		QString line = in.readLine();
		line = line.simplified();
		if (line.contains("[policy]"))
		{
			QString policyName = line.remove("[policy]");
			policyName = policyName.simplified();
			mScanRanges.push_back(ScanRange(policyName));
			curPolicyID = mScanRanges.size() - 1;
			//mSelectPolicies[curPolicyID].name = policyName;
			continue;
		}
		if (line.contains("red_bible_begin_ist"))
		{
			QString value = line.remove("red_bible_begin_ist");
			value = value.simplified();
			value = value.remove("=");
			value = value.simplified();
			mScanRanges[curPolicyID].redBibleBeginList = value.toInt();
			continue;
		}
		if (line.contains("red_bible_end_ist"))
		{
			QString value = line.remove("red_bible_end_ist");
			value = value.simplified();
			value = value.remove("=");
			value = value.simplified();
			mScanRanges[curPolicyID].redBibleEndList = value.toInt();
			continue;
		}
	}

	if (mScanRanges.size() == 0)
	{
		cout<<"ReciteAnalysisDialog::loadScanRange-->no scan range policy available."<<endl;
		QCoreApplication::quit();
	}
	else
	{
		ui.scanRangeComboBox->clear();
		int lastPolicy = OPT.scanRangePolicy;
		if (lastPolicy >= mScanRanges.size())
			lastPolicy = 0;

		for (int i=0; i<mScanRanges.size(); i++)
			ui.scanRangeComboBox->addItem(mScanRanges[i].name);

		ui.listBeginInput->setValue(mScanRanges[lastPolicy].redBibleBeginList);
		ui.listEndInput->setValue(mScanRanges[lastPolicy].redBibleEndList);
		ui.scanRangeComboBox->setCurrentIndex(lastPolicy);
	}
}

void ReciteAnalysisDialog::loadSelectPolicies()
{
	cout<<"ReciteAnalysisDialog::loadSelectPolicies"<<endl;
	mSelectPolicies.clear();

	QFile file("./configure/ScanPolicy.conf");
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		cout<<"ReciteAnalysisDialog::loadScanPolicies-->no ScanPolicy.conf. Create a new one."<<endl;
		createDefaultSelectPolicyFile();

		if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
		{
			cout<<"ReciteAnalysisDialog::loadScanPolicies-->failed to create a new SelectPolicy.conf."<<endl;
			QCoreApplication::quit();
		}
	}

	QTextCodec* codec = QTextCodec::codecForName("utf8");
	QTextStream in(&file);
	in.setCodec(codec);

	int curPolicyID = 0;
	while (!in.atEnd())
	{
		QString line = in.readLine();
		line = line.simplified();
		if (line.contains("[policy]"))
		{
			QString policyName = line.remove("[policy]");
			policyName = policyName.simplified();
			mSelectPolicies.push_back(SelectPolicy(policyName));
			curPolicyID = mSelectPolicies.size() - 1;
			//mSelectPolicies[curPolicyID].name = policyName;
			continue;
		}
		if (line.contains("difficulty_level_max"))
		{
			QString value = line.remove("difficulty_level_max");
			value = value.simplified();
			value = value.remove("=");
			value = value.simplified();
			mSelectPolicies[curPolicyID].difficultyLevelMax = value.toInt();
			continue;
		}
		if (line.contains("difficulty_level_min"))
		{
			QString value = line.remove("difficulty_level_min");
			value = value.simplified();
			value = value.remove("=");
			value = value.simplified();
			mSelectPolicies[curPolicyID].difficultyLevelMin = value.toInt();
			continue;
		}
		if (line.contains("active_finished_word_count_max"))
		{
			QString value = line.remove("active_finished_word_count_max");
			value = value.simplified();
			value = value.remove("=");
			value = value.simplified();
			mSelectPolicies[curPolicyID].activeFinishedWordCountMax = value.toInt();
			continue;
		}
		if (line.contains("active_reciting_word_count_max"))
		{
			QString value = line.remove("active_reciting_word_count_max");
			value = value.simplified();
			value = value.remove("=");
			value = value.simplified();
			mSelectPolicies[curPolicyID].activeRecitingWordsCountMax = value.toInt();
			continue;
		}
		if (line.contains("active_untouched_word_count_max"))
		{
			QString value = line.remove("active_untouched_word_count_max");
			value = value.simplified();
			value = value.remove("=");
			value = value.simplified();
			mSelectPolicies[curPolicyID].activeUntouchedWordsCountMax = value.toInt();
			continue;
		}
	}

	if (mSelectPolicies.size() == 0)
	{
		cout<<"ReciteAnalysisDialog::loadScanPolicies-->no scan policy available."<<endl;
		QCoreApplication::quit();
	}
	else
	{
		ui.selectPolicyBox->clear();
		int lastPolicy = OPT.selectWordPolicy;
		if (lastPolicy >= mSelectPolicies.size())
			lastPolicy = 0;

		for (int i=0; i<mSelectPolicies.size(); i++)
			ui.selectPolicyBox->addItem(mSelectPolicies[i].name);

		ui.difficultyLevelMaxInput->setValue(mSelectPolicies[lastPolicy].difficultyLevelMax);
		ui.difficultyLevelMinInput->setValue(mSelectPolicies[lastPolicy].difficultyLevelMin);
		ui.activeFinishedWordsCount->setValue(mSelectPolicies[lastPolicy].activeFinishedWordCountMax);
		ui.activeRecitingWordsCount->setValue(mSelectPolicies[lastPolicy].activeRecitingWordsCountMax);
		ui.activeUntouchedWordsCount->setValue(mSelectPolicies[lastPolicy].activeUntouchedWordsCountMax);
		ui.selectPolicyBox->setCurrentIndex(lastPolicy);
	}
}

void ReciteAnalysisDialog::selectPolicyChanged(int index)
{
	if (index != OPT.selectWordPolicy)
	{
		ui.difficultyLevelMaxInput->setValue(mSelectPolicies[index].difficultyLevelMax);
		ui.difficultyLevelMinInput->setValue(mSelectPolicies[index].difficultyLevelMin);
		ui.activeFinishedWordsCount->setValue(mSelectPolicies[index].activeFinishedWordCountMax);
		ui.activeRecitingWordsCount->setValue(mSelectPolicies[index].activeRecitingWordsCountMax);
		ui.activeUntouchedWordsCount->setValue(mSelectPolicies[index].activeUntouchedWordsCountMax);
		OPT.selectWordPolicy = index;
	}
}

void ReciteAnalysisDialog::scanRangeChanged(int index)
{
	if (index != OPT.scanRangePolicy)
	{
		ui.listBeginInput->setValue(mScanRanges[index].redBibleBeginList);
		ui.listEndInput->setValue(mScanRanges[index].redBibleEndList);
		OPT.scanRangePolicy = index;
	}
}

void ReciteAnalysisDialog::selectRowAsCandidates()
{
	cout<<"ReciteAnalysisDialog::selectRowAsCandidates"<<endl;
	vector<int>& candidates = ui.scanResultTableView->getSelectedRows();
	for (int i=candidates.size()-1; i>=0; i--)
	{
		QList<QStandardItem*>curRow = mWordDateModel->takeRow(candidates[i]);
		mCandidatesModel->insertRow(mCandidatesModel->rowCount(), curRow);
		cout<<"Add row: "<<candidates[i]+1<<endl;
	}

	mCandidatesModel->sort(2);
	mCandidatesModel->sort(1);

	ui.selectedWordGroupTableView->setModel(mCandidatesModel);
	ui.selectedWordGroupTableView->resizeColumnsToContents();
	ui.selectedWordGroupTableView->verticalHeader()->hide();
}

void ReciteAnalysisDialog::removeRowAsCandidates()
{
	vector<int>& removeWords = ui.selectedWordGroupTableView->getSelectedRows();
	for (int i=removeWords.size()-1; i>=0; i--)
	{
		QList<QStandardItem*>curRow = mCandidatesModel->takeRow(removeWords[i]);
		mWordDateModel->insertRow(mWordDateModel->rowCount(), curRow);
	}

/*	while (ui.selectedWordGroupTableView->getFirstSelected() != -1)
	{
		int firstRow = ui.selectedWordGroupTableView->getFirstSelected();
		QList<QStandardItem*>curRow = mCandidatesModel->takeRow(firstRow);
		mWordDateModel->insertRow(mWordDateModel->rowCount(), curRow);
	}
*/
	mWordDateModel->sort(2);
	mWordDateModel->sort(1);
}

void ReciteAnalysisDialog::clearAllRowCandidates()
{
	int rowCount = mCandidatesModel->rowCount();
	for (int i=0; i<rowCount; i++)
	{
		//cout<<"ReciteAnalysisDialog::selectRowAsCandidates-->take row "<<rowIndex[i]<<endl;
		QList<QStandardItem*>curRow = mCandidatesModel->takeRow(0);
		mWordDateModel->insertRow(0, curRow);
	}

	//ui.selectedWordGroupTableView->clear();
	mWordDateModel->sort(2);
	mWordDateModel->sort(1);
}

void ReciteAnalysisDialog::selectWordsToRecite()
{
	cout<<"ReciteAnalysisDialog::selectWordsToRecite"<<endl;
	mFinishedWordIDs.clear();
	ui.selectResultsDisplay->clear();

	if (mRecitingWordIDs.empty() && mNewWordIDs.empty() && mFinishedWordGroupCount.empty())
	{
		cout<<"ReciteAnalysisDialog::selectWordsToRecite-->Error! All id vectors are empty."<<endl;
		QCoreApplication::quit();
	}

	int difficultyLevelMax = ui.difficultyLevelMaxInput->value();
	int difficultyLevelMin = ui.difficultyLevelMinInput->value();

	QString command;

	//cout	<<"ReciteAnalysisDialog::selectWordsToRecite-->"<<endl;
	for (int i=0; i<mCandidatesModel->rowCount(); i++)
	{
		QString type = mCandidatesModel->item(i, 0)->text();
		int interval = mCandidatesModel->item(i, 1)->text().toInt();
		int reciteCount = mCandidatesModel->item(i, 3)->text().toInt();

		//cout	<<"type="<<type.toUtf8().data()
		//		<<"\tinterval="<<interval
		//		<<"\treciteCount="<<reciteCount<<endl;
		command.clear();
		command.append(QString("select analysis.id,history.importance_level,history.difficulty_level from history,analysis,lists "));
		command.append(QString("where history.id = analysis.id and history.id=lists.id and analysis.last_interval=%1 and ").arg(interval));
		command.append(QString("analysis.recite_count=%1 and ").arg(reciteCount));
		command.append(QString("lists.red_bible_list >=%1 and ").arg(ui.listBeginInput->text().toInt()));
		command.append(QString("lists.red_bible_list <=%1; ").arg(ui.listEndInput->text().toInt()));
		//cout<<command.toUtf8().data()<<endl;
		DB.quickGet(command);
		for (int j=0; j<DB.getRowCount(); j++)
		{
			int id = DB.getTableValue(j,"id").toInt();
			int impLevel = DB.getTableValue(j,"importance_level").toInt();
			int diffLevel = DB.getTableValue(j,"difficulty_level").toInt();
			//cout	<<"ReciteAnalysisDialog::selectWordsToRecite-->id="<<id
			//		<<"\timpLevel="<<impLevel
			//		<<"\tdiffLevel="<<diffLevel<<endl;

			/*if (	impLevel >= importanceLevelMin
			&&	impLevel <= importanceLevelMax*/
			if(		diffLevel >= difficultyLevelMin
			&&	diffLevel <= difficultyLevelMax)
			{
				//cout<<"ReciteAnalysisDialog::selectWordsToRecite-->push_back:"<<id<<endl;
				mFinishedWordIDs.push_back(id);
			}
		}
	}

	int activeFinishedWordCount = ui.activeFinishedWordsCount->value();
	int activeRecitingWordsCount = ui.activeRecitingWordsCount->value();
	int activeUntouchedWordsCount = ui.activeUntouchedWordsCount->value();

	if(activeFinishedWordCount > mFinishedWordIDs.size())
		activeFinishedWordCount = mFinishedWordIDs.size();

	if(activeRecitingWordsCount > mRecitingWordIDs.size())
		activeRecitingWordsCount = mRecitingWordIDs.size();

	if(activeUntouchedWordsCount > mNewWordIDs.size())
		activeUntouchedWordsCount = mNewWordIDs.size();

	vector<int> finishedWordIDs = mFinishedWordIDs;
	vector<int> recitingWordIDs = mRecitingWordIDs;
	vector<int> newWordIDs = mNewWordIDs;

	mFinalIDs.clear();
	srand((int)time(0));
	vector<int>::iterator id_it;
	for(int i=0; i<activeFinishedWordCount; i++)
	{
		int wordIndex = 0+(int)(float(finishedWordIDs.size())*rand()/(RAND_MAX+1.0));
		mFinalIDs.push_back(finishedWordIDs[wordIndex]);
		id_it = finishedWordIDs.begin() + wordIndex;
		finishedWordIDs.erase(id_it);
	}

	for(int i=0; i<activeRecitingWordsCount; i++)
	{
		int wordIndex = 0+(int)(float(recitingWordIDs.size())*rand()/(RAND_MAX+1.0));
		mFinalIDs.push_back(recitingWordIDs[wordIndex]);
		id_it = recitingWordIDs.begin() + wordIndex;
		recitingWordIDs.erase(id_it);
	}

	for(int i=0; i<activeUntouchedWordsCount; i++)
	{
		int wordIndex = 0+(int)(float(newWordIDs.size())*rand()/(RAND_MAX+1.0));
		mFinalIDs.push_back(newWordIDs[wordIndex]);
		id_it = newWordIDs.begin() + wordIndex;
		newWordIDs.erase(id_it);
	}

	//cout<<"ReciteAnalysisDialog::selectWordsToRecite-->mFinalIDs.size() = "<<mFinalIDs.size()<<endl;
	//ui.selectWordButton->setEnabled(false);
	QString result;
	result.append(QString("Select %1(").arg(activeFinishedWordCount));
	result.append(QString("%1) 'finished' words.<br>").arg(mFinishedWordIDs.size()));
	result.append(QString("Select %1(").arg(activeRecitingWordsCount));
	result.append(QString("%1) 'reciting' words.<br>").arg(mRecitingWordIDs.size()));
	result.append(QString("Select %1(").arg(activeUntouchedWordsCount));
	result.append(QString("%1) 'new' words.<br>").arg(mNewWordIDs.size()));
	ui.selectResultsDisplay->insertHtml(result);
}

void ReciteAnalysisDialog::quickSelectDifficultWords()
{
	//cout<<"ReciteAnalysisDialog::quickSelectDifficultWords"<<endl;
	int finialSelectWordCount = ui.quickSelectWordCountInput->value();
	OPT.quickSelectWordCount = ui.quickSelectWordCountInput->value();

	mFinalIDs.clear();
	QString command = QString(
			"select history.id, history.difficulty_level, history.last_reciting_status, analysis.last_interval"
			" from history,analysis "
			" where history.id = analysis.id ");
	command.append(QString(" and ( history.last_reciting_status = %1 or analysis.last_interval > 0) ").arg(WS_Reciting));
	command.append(QString(" order by difficulty_level DESC;"));
	DB.quickGet(command);
	int diffMin = 9999;
	int diffMax = 0;
	for (int i=0; i<DB.getRowCount() && i < finialSelectWordCount; i++)
	{
		int id = DB.getTableValue(i, "id").toInt();
		int diffVal = DB.getTableValue(i, "difficulty_level").toInt();
		mFinalIDs.push_back(id);
		if (diffMin > diffVal) diffMin = diffVal;
		if (diffMax < diffVal) diffMax = diffVal;
	}
	cout<<"ReciteAnalysisDialog::quickSelectDifficultWords-->diffMin="<<diffMin<<endl;
	cout<<"ReciteAnalysisDialog::quickSelectDifficultWords-->diffMax="<<diffMax<<endl;

	/*mFinalIDs.clear();
	int wordCount=0;
	int diffAvg = 0;
	int diffMin = 9999;
	int diffMax = 0;
	for (	map<int, vector<int> >::iterator word_it = mQuickSelectWords.end();
			word_it != mQuickSelectWords.begin();
			word_it--)
	{
		word_it--;
		//cout<<word_it->first<<"\t"<<word_it->second<<endl;
		for (int i=0; i<word_it->second.size(); i++)
		{
			mFinalIDs.push_back(word_it->second[i]);
			//cout<<"ReciteAnalysisDialog::quickSelectDifficultWords-->push_back("<<word_it->second[i]<<"["<<word_it->first<<"])"<<endl;
			//cout<<"push_back:"<<word_it->second[i]<<"\tdiffLevel="<<word_it->first<<endl;
			wordCount++;
			diffAvg += word_it->first;
			if (diffMin > word_it->first) diffMin = word_it->first;
			if (diffMax < word_it->first) diffMax = word_it->first;
			if (wordCount == finialSelectWordCount)
				break;
		}

		if (wordCount == finialSelectWordCount)
			break;
	}

	cout<<"ReciteAnalysisDialog::quickSelectDifficultWords-->diffMin="<<diffMin<<endl;
	cout<<"ReciteAnalysisDialog::quickSelectDifficultWords-->diffMax="<<diffMax<<endl;
	cout<<"ReciteAnalysisDialog::quickSelectDifficultWords-->diffAvg="<<diffAvg/finialSelectWordCount<<endl;
*/
	recite();
}

void ReciteAnalysisDialog::setTask(WordManagerTaskType type)
{
	WM.finishLastTask();

	QString queryCommand;
	DB.quickUpdate("BEGIN");
	for(int i=0; i<mFinalIDs.size(); i++)
	{
		int wordID = mFinalIDs[i];
		queryCommand.clear();
		queryCommand.append(QString("update history set is_active = 1 where id = %1;").arg(wordID));
		DB.quickUpdate(queryCommand);
	}
	DB.quickUpdate("COMMIT");

	WordManagerTask task;
	task.taskType = type;
	task.selectWordMethod = select_by_active_status;
	task.listName = "";
	task.listType = none;
	WM.setTask(task);
}

void ReciteAnalysisDialog::recite()
{
	//cout<<"ReciteAnalysisDialog::recite-->mFinalIDs.size() = "<<mFinalIDs.size()<<endl;

	if(mFinalIDs.size() > 20)
	{
		setTask(TaskType_Recite);
		emit reciteSelectedWords();
		this->accept();
	}
	else
	{
		QString message;
		message = "The amount of reviewing words is not adequate. "
				"Please refine your scan policy or active words count.";
		MessageBox::showWarning(QMessageBox::Warning,QString("Warning!"),message,12,this);
	}
}

void ReciteAnalysisDialog::browse()
{
	//WM.finishLastTask();
	if(mFinalIDs.size() > 20)
	{
		setTask(TaskType_Browse);
		emit browseSelectedWords();
		this->accept();
	}
	else
	{
		QString message;
		message = "The amount of reviewing words is not adequate. "
				"Please refine your scan policy or active words count.";
		MessageBox::showWarning(QMessageBox::Warning,QString("Warning!"),message,12,this);
	}
}
